# Get started [An introduction to Ponder]

## What is Ponder?

Ponder is an open-source framework for custom Ethereum indexing.

You write TypeScript code to transform onchain data into your application's schema. Then, Ponder fetches data from the chain, runs your indexing logic, and writes the result to Postgres.

Once indexed, you can query the data through GraphQL, SQL over HTTP, or directly in Postgres.

## Quickstart

::::steps

### Run `create-ponder`

The quickest way to create a new Ponder project is `create-ponder`, which sets up everything automatically for you.

:::code-group

```bash [pnpm]
pnpm create ponder
```

```bash [yarn]
yarn create ponder
```

```bash [npm]
npm init ponder@latest
```

:::

On installation, you'll see a few prompts.

:::code-group

```ansi [Default]
✔ What's the name of your project? › new-project
✔ Which template would you like to use? › Default

✔ Installed packages with pnpm.
✔ Initialized git repository.
```

```ansi [ERC-20 example]
✔ What's the name of your project? › new-project
✔ Which template would you like to use? › Reference - ERC20 token

✔ Installed packages with pnpm.
✔ Initialized git repository.
```

```ansi [Etherscan template]
✔ What's the name of your project? › new-project
✔ Which template would you like to use? › Etherscan contract link
✔ Enter a block explorer contract url › https://basescan.org/address/0x3bf93770f2d4a794c3d9ebefbaebae2a8f09a5e5

✔ Fetched contract metadata from basescan.org.
✔ Installed packages with pnpm.
✔ Initialized git repository.
```

```ansi [Subgraph template]
✔ What's the name of your project? › new-project
✔ Which template would you like to use? › Subgraph ID
✔ Which provider is the subgraph deployed to? › The Graph
✔ Enter a subgraph Deployment ID › QmWSfh5qnidd1tBd6FY6eDvbSQUPTNhNG7o8CUGenvbaht

✔ Fetched subgraph metadata for QmWSfh5qnidd1tBd6FY6eDvbSQUPTNhNG7o8CUGenvbaht.
✔ Installed packages with pnpm.
✔ Initialized git repository.
```

:::

This guide follows the ERC-20 example, which indexes a token contract on Ethereum mainnet.

### Start the dev server

After installation, start the local development server.

:::code-group

```bash [pnpm]
pnpm dev
```

```bash [yarn]
yarn dev
```

```bash [npm]
npm run dev
```

:::

Ponder will connect to the database, start the HTTP server, and begin indexing.

:::code-group

```ansi [Logs]
11:34:39 AM INFO  build      Using PGlite database at .ponder/pglite (default)
11:34:39 AM INFO  database   Using database schema 'public'
11:34:40 AM INFO  database   Created tables [account, transfer_event, allowance, approval_event]
11:34:40 AM INFO  server     Started listening on port 42069
11:34:40 AM INFO  server     Started returning 200 responses from /health endpoint
11:34:56 AM INFO  app        Indexed 12 events with 4.1% complete
```

```ansi [Terminal UI]
Sync

│ Network │ Status            │ Block    │ RPC (req/s) │
├─────────┼───────────────────┼──────────┼─────────────┤
│ mainnet │ historical (9.2%) │ 13143062 │        21.4 │

Indexing

│ Event          │ Count │ Duration (ms) │
├────────────────┼───────┼───────────────┤
│ ERC20:Transfer │    12 │         0.123 │
│ ERC20:Approval │     0 │             - │

Progress (historical)

███░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 7.4% (2m 59s eta)
```

:::

{/* :::info
By default, Ponder uses public RPC endpoints and writes data to PGlite, an embedded Postgres database. You can change these settings later.
::: */}

### Query the database

Visit [localhost:42069/graphql](http://localhost:42069/graphql) in your browser to explore the auto-generated GraphQL API. Here's a query for the top accounts by balance, along with the total number of accounts.

:::code-group

```graphql [Query]
query {
  accounts(orderBy: "balance", orderDirection: "desc", limit: 2) {
    items {
      address
      balance
    }
    totalCount
  }
}
```

```json [Result]
{
  "accounts": {
    "items": [
      { "address": "0x1234567890123456789012345678901234567890", "balance": "1000000000000000000" },
      { "address": "0x1234567890123456789012345678901234567891", "balance": "900000000000000000" },
    ],
    "totalCount": 1726,
  }
}
```

:::

:::tip
You can also query Ponder tables directly in Postgres, or write custom API endpoints. [Read more](/docs/query/direct-sql).
:::

### Customize the schema

Let's add a new column to a table in `ponder.schema.ts`. We want to track which accounts are an owner of the token contract.

```ts [ponder.schema.ts]
import { index, onchainTable, primaryKey, relations } from "ponder";

export const account = onchainTable("account", (t) => ({
  address: t.hex().primaryKey(),
  balance: t.bigint().notNull(),
  isOwner: t.boolean().notNull(), // [!code ++]
}));

// ...
```

Immediately, there's a type error in `src/index.ts` and a runtime error in the terminal. We added a required column, but our indexing logic doesn't include it.

```ansi [Terminal]
12:16:16 PM ERROR indexing   Error while processing 'ERC20:Transfer' event
NotNullConstraintError: Column 'account.isOwner' violates not-null constraint.
    at /workspace/new-project/src/index.ts:10:3
   8 |
   9 | ponder.on("ERC20:Transfer", async ({ event, context }) => {
> 10 |   await context.db
     |   ^
  11 |     .insert(account)
  12 |     .values({ address: event.args.from, balance: 0n })
  13 |     .onConflictDoUpdate((row) => ({
```

### Update indexing logic

Update the indexing logic to include `isOwner` when inserting new rows into the `account` table.

```ts [src/index.ts]
import { ponder } from "ponder:registry";
import { account } from "ponder:schema";

const OWNER_ADDRESS = "0x3bf93770f2d4a794c3d9ebefbaebae2a8f09a5e5"; // [!code ++]

ponder.on("ERC20:Transfer", async ({ event, context }) => {
  await context.db
    .insert(account)
    .values({
      address: event.args.from,
      balance: 0n,
      isOwner: event.args.from === OWNER_ADDRESS, // [!code ++]
    })
    .onConflictDoUpdate((row) => ({
    // ...
})
```

As soon as we save the file, the dev server hot reloads and finishes indexing successfully.

:::code-group

```ansi [Logs]
12:27:26 PM INFO  build      Hot reload 'src/index.ts'
12:27:29 PM INFO  app        Indexed 4999 events with 54.2% complete and 471ms remaining
12:27:29 PM INFO  app        Indexed 4999 events with 75.5% complete and 276ms remaining
12:27:29 PM INFO  app        Indexed 4998 events with 99.4% complete and 6ms remaining
12:27:29 PM INFO  app        Indexed 108 events with 100% complete and 0ms remaining
12:27:29 PM INFO  indexing   Completed historical indexing
```

```ansi [Terminal UI]
Sync

│ Network │ Status   │ Block    │ RPC (req/s) │
├─────────┼──────────┼──────────┼─────────────┤
│ mainnet │ complete │ 13149000 │         0.0 │

Indexing

│ Event          │ Count │ Duration (ms) │
├────────────────┼───────┼───────────────┤
│ ERC20:Transfer │ 11626 │         0.011 │
│ ERC20:Approval │  3478 │         0.008 │

Progress (live)

████████████████████████████████████████████████ 100%
```

:::

::::


## Next steps

This quickstart only scratches the surface of what Ponder can do. Take a look at the [examples directory](https://github.com/ponder-sh/ponder/tree/main/examples) for more complex projects, or the [GitHub dependents](https://github.com/ponder-sh/ponder/network/dependents?package_id=UGFja2FnZS0xMzA2OTEyMw%3D%3D) for a list of real-world repositories using Ponder.

Or, continue reading the guides and API reference here on the documentation site.

* [Contract configuration](/docs/config/contracts)
* [Query the database directly](/docs/query/direct-sql)
* [Schema design](/docs/schema/tables)
